<template>
  <div id="xterm" class="terminal" />
</template>

<script>
import { Terminal } from 'xterm' // 导入 xterm 包，用于创建和操作终端对象
import { common as xtermTheme } from 'xterm-style' // 导入 xterm 样式主题
import 'xterm/css/xterm.css' // 导入 xterm CSS 样式
import { FitAddon } from 'xterm-addon-fit' // 导入 xterm fit 插件，用于调整终端大小
import { WebLinksAddon } from 'xterm-addon-web-links' // 导入 xterm web-links 插件，可以捕获 URL 并将其转换为可点击链接
import 'xterm/lib/xterm.js' // 导入 xterm 库

export default {
  name: "terminal",
  data() {
    return {
      term: "",//terminal 黑窗口容器
      socket:"",
      selectObj:1,
      prefix: "[root@serverip ~]# ",//前缀
      inputText: "",//输入内容，每次回车后进行ws通信然后清空此数据
      socketURI:"ws://localhost:8080/ws",
      form:{
        ip:"1",
        user:"pass",
        password:"asdf",
        "cmd":"a"
      }
    };
  },
  mounted() {
    this.InitCon()
  },
  beforeDestroy() {
    this.socket.close()
    this.term.dispose()
  },
  methods:{
    async InitTerm(){
      this.term = new Terminal(
          {
            theme: xtermTheme,
            rendererType: 'canvas',
            convertEol: true,
            cursorBlink: true
          }
      );
      const fitAddon = new FitAddon()
      fitAddon.fit()
      this.term.loadAddon(fitAddon)
      this.term.loadAddon(new WebLinksAddon())

      this.term.open(document.getElementById('xterm'));
      this.term.write(this.prefix); //黑窗口 前缀
      await this.termPromt(); //term.promt
      await this.termKeyCode(); //事件
    },
    InitCon(){
      this.socket = new WebSocket(this.socketURI+'?ip=' + this.form.ip + '&user=' + this.form.user + '&password=' + this.form.password + '&cmd=' + this.form.cmd)
      this.socketOnClose();
      this.socketOnOpen();
      this.socketOnError();
      this.socket.onmessage = (evt) => {
        this.term.write(evt.data)
      }
    },
    socketOnOpen() {
      this.socket.onopen = () => {
        // 链接成功后
        this.InitTerm()
      }
    },
    socketOnClose() {
      this.socket.onclose = () => {
        this.socket.close()
      }
    },
    socketOnError() {
      this.socket.onerror = () => {
        alert("链接断开")
        this.socket.close()
      }
    },
    termKeyCode() {
      const TERMINAL_INPUT_KEY = {
        BACK: 8, // 退格删除键
        ENTER: 13, // 回车键
        UP: 38, // 方向盘上键
        DOWN: 40, // 方向盘键
        LEFT: 37, // 方向盘左键
        RIGHT: 39, // 方向盘右键
      };
      const { eqpCode, server } = this.selectObj;

      let inputText = "";
      let currentIndex = 0;
      let inputTextList = [];
      this.term.onKey((e) => {
        const { key, domEvent } = e;
        const { keyCode, altKey, altGraphKey, ctrlKey, metaKey } = domEvent;

        const printAble = !(altKey || altGraphKey || ctrlKey || metaKey); // 禁止相关按键
        const totalOffsetLength = inputText.length + this.prefix.length; // 总偏移量
        const currentOffsetLength = this.term._core.buffer.x; // 当前x偏移量

        switch (keyCode) {
            //删除
          case TERMINAL_INPUT_KEY.BACK:
            if (currentOffsetLength > this.prefix.length) {
              const cursorOffSetLength = this.getCursorOffsetLength(totalOffsetLength - currentOffsetLength, "\x1b[D"); // 保留原来光标位置

              this.term._core.buffer.x = currentOffsetLength - 1;
              this.term.write("\x1b[?K" + inputText.slice(currentOffsetLength - this.prefix.length));
              this.term.write(cursorOffSetLength);
              inputText = `${inputText.slice(0, currentOffsetLength - this.prefix.length - 1)}${inputText.slice(
                  currentOffsetLength - this.prefix.length
              )}`;
            }
            break;
            //回车
          case TERMINAL_INPUT_KEY.ENTER: {
            this.term.write("\r\n");

            console.log("inputText", inputText);

            this.socket.send(inputText)

            if (!inputText.trim()) {
              this.term.prompt();
              return;
            }

            if (inputTextList.indexOf(inputText) === -1) {
              inputTextList.push(inputText);
              currentIndex = inputTextList.length;
            }

            this.term.prompt();
            inputText = "";
            break;
          }

          case TERMINAL_INPUT_KEY.UP: {
            if (!inputTextList[currentIndex - 1]) break;

            const offsetLength = this.getCursorOffsetLength(inputText.length, "\x1b[D");

            inputText = inputTextList[currentIndex - 1];
            this.term.write(offsetLength + "\x1b[?K");
            this.term.write(inputTextList[currentIndex - 1]);
            this.term._core.buffer.x = totalOffsetLength;
            currentIndex--;
            break;
          }
          case TERMINAL_INPUT_KEY.LEFT:
            if (currentOffsetLength > this.prefix.length) {
              this.term.write(key); // '\x1b[D'
            }
            break;

          case TERMINAL_INPUT_KEY.RIGHT:
            if (currentOffsetLength < totalOffsetLength) {
              this.term.write(key); // '\x1b[C'
            }
            break;
          default: {
            // 在当前的坐标写上 key 和坐标后面的字符
            // 移动停留在当前位置的光标
            if (!printAble) break;
            if (totalOffsetLength >= this.term.cols) break;
            if (currentOffsetLength >= totalOffsetLength) {
              this.term.write(key);
              inputText += key;
              break;
            }
            let cursorOffSetLength = this.getCursorOffsetLength(totalOffsetLength - currentOffsetLength, "\x1b[D");
            this.term.write("\x1b[?K" + `${key}${inputText.slice(currentOffsetLength - this.prefix.length)}`);
            this.term.write(cursorOffSetLength);
            inputText = inputText.slice(0, currentOffsetLength) + key + inputText.slice(totalOffsetLength - currentOffsetLength);
            break;
          }
        }
      });
    },
    //限制和后端交互，只有输入回车键才显示结果
    termPromt() {
      this.term.prompt = () => {
        this.term.write(this.prefix);
      };
    },
    //获取光标当前位置
    getCursorOffsetLength(offsetLength, subString) {
      let cursorOffsetLength = "";
      for (let offset = 0; offset < offsetLength; offset++) {
        cursorOffsetLength += subString;
      }
      return cursorOffsetLength;
    },
  }
};
</script>


<style scoped>
#xterm {
  width: 100%;
  height: 100%;
  font-family: 'Courier New', Courier, monospace;
}
</style>
